#include <iostream>

#include "libpan.hpp"
#include "login.hpp"

using namespace std;

extern "C" void _declspec(dllexport) Callback(GunZPacket *packet)
{
	if(packet->GetType() == GUNZPACKET_KEY_EXCHANGE)
		Login(GetUsername(), GetPassword(), GetChecksum(), GetMD5(), GetMD5Size());

	if(packet->GetCommandID())
	{
		int result;

		switch(packet->GetCommandID())
		{
			case MATCH_RESPONSEACCOUNTCHARLIST:						
				unsigned int encrypted_characters;
				
				packet->GetBytes(reinterpret_cast<unsigned char *>(&encrypted_characters), sizeof(encrypted_characters));
				encrypted_characters = (encrypted_characters - 8) / 34;

				unsigned int junk;
				packet->GetBytes(reinterpret_cast<unsigned char *>(&junk), sizeof(junk));

				unsigned int characters;
				packet->GetBytes(reinterpret_cast<unsigned char *>(&characters), sizeof(characters));

				if(characters == encrypted_characters)
				{
					if(characters > 0)
					{
						char names[4][SIZE_1K];
						char names_tmp[4][SIZE_1K];

						unsigned char indexes[4];
						unsigned char indexes2[4];
						unsigned char levels[4];

						for(int i = 0; i < (int)characters; i++)
						{
							memset(names_tmp[i], 0, sizeof(names_tmp[i]));

							for(int j = 0;; j++)
							{
								packet->GetBytes(reinterpret_cast<unsigned char *>(&names_tmp[i][j]), sizeof(char));

								if(!names_tmp[i][j])
									break;
							}
							
							char *padding = new char[31 - strlen(names_tmp[i])];

							memset(padding, 0, sizeof(padding));
							packet->GetBytes(reinterpret_cast<unsigned char *>(padding), 31 - strlen(names_tmp[i]));

							delete padding;

							unsigned char index;
							packet->GetBytes(reinterpret_cast<unsigned char *>(&index), sizeof(index));

							unsigned char level;
							packet->GetBytes(reinterpret_cast<unsigned char *>(&level), sizeof(level));

							memset(names[index], 0, sizeof(names[index]));
							memcpy(names[index], names_tmp[i], sizeof(names[index]));
							indexes2[index] = index;
							indexes[index] = 1;
							levels[index] = level;
						}

						bool found = false;

						for(int i = 0; i < 4; i++)
						{
							if(!_stricmp(names[i], GetCharacterName()))
							{
								found = true;
								SelectCharacter(GetPlayerID(), i);

								break;
							}
						}

						if((characters >= 4) && !found)
						{
							Print("Login: Character not found: %s\n", GetCharacterName());
							Print("Login: You have 4 characters, delete one? (y/n) ");

							unsigned char answer;

							cin >> answer;
							cin.get();

							while((answer != 'Y') && (answer != 'y') && (answer != 'N') && (answer != 'n'))
							{									
								Print("Login: Invalid response detected\n");
								Print("Login: You have 4 characters, delete one? (y/n) ");

								cin >> answer;
								cin.get();
							}

							if((answer == 'Y') || (answer == 'y'))
							{					
								for(int i = 0; i < 4; i++)
								{						
									Print("Login: - Index: %d\n", indexes2[i]);
									Print("Login: - Name: %s\n", names[i]);
									Print("Login: - Level: %d\n", levels[i]);

									if(i < (int)(characters - 1))
										Print("Login: - ----------------------\n");
								}

								Print("Login: Enter the character index you wish to delete: ");

								cin >> answer;
								cin.get();

								unsigned char ind;

								if(answer == '0')
									ind = 0;

								else if(answer == '1')
									ind = 1;

								else if(answer == '2')
									ind = 2;

								else if(answer == '3')
									ind = 3;
								else
									ind = characters;

								while(ind >= characters)
								{										
									Print("Login: Invalid response detected\n");
									Print("Login: Enter the character index you wish to delete: ");

									cin >> answer;
									cin.get();

									if(answer == '0')
										ind = 0;

									else if(answer == '1')
										ind = 1;

									else if(answer == '2')
										ind = 2;

									else if(answer == '3')
										ind = 3;

									else
										ind = characters;
								}

								Print("Login: Attempting to delete character: %s\n", names[ind]);
								DeleteCharacter(GetPlayerID(), ind, names[ind]);	

								Print("Login: Attempting to create character: %s\n", GetCharacterName());
								CreateCharacter(GetPlayerID(), ind, GetCharacterName(), 1, 4, 0, 0);
							}
						}
						else if((characters < 4) && !found)
						{
							Print("Login: Character not found: %s\n", GetCharacterName());
							Print("Login: Attempting to create character: %s\n", GetCharacterName());

							for(int i = 0; i < 4; i++)
							{						
								if(!indexes[i])
								{
									CreateCharacter(GetPlayerID(), i, GetCharacterName(), 1, 4, 0, 0);

									break;
								}
							}
						}
					}
					else
					{
						Print("Login: Character not found: %s\n", GetCharacterName());
						Print("Login: Attempting to create character: %s\n", GetCharacterName());

						CreateCharacter(GetPlayerID(), 0, GetCharacterName(), 1, 4, 0, 0);
					}
				}

				break;
					
			case MATCH_RESPONSECREATECHAR:
				packet->GetBytes(reinterpret_cast<unsigned char *>(&result), sizeof(result));

				switch(result)
				{
					case RESPONSE_OK:
						GetCharacterList();

						break;

					default:
						break;
				}

				break;

			case MATCH_RESPONSELOGIN:
				packet->GetBytes(reinterpret_cast<unsigned char *>(&result), sizeof(result));

				switch(result)
				{
					case RESPONSE_OK:
						GetCharacterList();

						break;

					default:
						break;
				}

				break;

			case MATCHSERVER_RESPONSERECOMMENDEDCHANNEL:	
				unsigned long id;

				packet->GetBytes(reinterpret_cast<unsigned char *>(&id), sizeof(id));
				packet->GetBytes(reinterpret_cast<unsigned char *>(&id), sizeof(id));
									
				JoinChannel(GetPlayerID(), id);
					
				break;

			case MATCH_RESPONSESELECTCHAR:										
				GetRecommendedChannel();
					
				break;

			default:
				break;
		}
	}
}

extern "C" void _declspec(dllexport) Init()
{
	char checksum[SIZE_1K];

	memset(username, 0, sizeof(username));
	memset(password, 0, sizeof(password));
	memset(character, 0, sizeof(character));
	memset(checksum, 0, sizeof(checksum));
	memset(md5, 0, sizeof(md5));

	ExtractFromINI(GetINIContents(), "[Login]", "Username = ", username);
	ExtractFromINI(GetINIContents(), "[Login]", "Password = ", password);
	ExtractFromINI(GetINIContents(), "[Login]", "Character = ", character);
	ExtractFromINI(GetINIContents(), "[Login]", "Checksum = ", checksum);
	ExtractFromINI(GetINIContents(), "[Login]", "MD5 = ", reinterpret_cast<char *>(md5));

	char byte[3];

	for(int i = 0, j = 0; i < 32; i += 2, j++)
	{
		byte[0] = md5[i];
		byte[1] = md5[i + 1];

		md5[j] = static_cast<unsigned char>(strtol(byte, 0, 16));
	}

	SetUsername(username);
	SetPassword(password);
	SetCharacterName(character);
	SetChecksum(_strtoui64(checksum, 0, 10));
	SetMD5(md5);
	SetMD5Size(16);

	Print("Login: Loaded config\n");
}